package liming.tool.maven.info;

import liming.tool.json.ToJson;
import liming.tool.json.annotation.JsonMark;
import liming.tool.json.annotation.JsonPermission;
import liming.tool.json.annotation.JsonType;
import liming.tool.maven.tool.XmlParser;
import org.json.JSONArray;
import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@JsonMark("mode")
public class Mode implements ToJson {
    Mode parent;
    @JsonMark(permissions = {JsonPermission.Getter,JsonPermission.AutoValue})
    String modeName;
    BaseModeConfig baseModeConfig;
    Resources resources;
    @JsonType(Dependency.class)
    @JsonMark
    List<Dependency> dependencies;
    @JsonType(Java.class)
    @JsonMark
    List<Java> javas;
    @JsonType(Mode.class)
    @JsonMark
    List<Mode> modes;

    Set<String> modeNames= new HashSet<>();


    Mode(){}
    public String getModeName() {
        return modeName;
    }

    public String getModePath(){
        if(parent==null)
            return modeName;
        return parent.getModePath() + File.separator + getModeName();
    }

    public String findModePath(String modeName) {
        if(this.modeName.equals(modeName))
            return getModePath();
        if(modeNames.contains(modeName)){
            for (Mode mode : modes)
                if(mode.modeNames.contains(modeName))
                    return mode.findModePath(modeName);
        }return "";
    }


    @Override
    public JSONObject serializeBody(Class<?> type) {
        JSONObject jsonBody = ToJson.super.serializeBody(type);
        baseModeConfig.serializeBody(BaseModeConfig.class).toMap()
                .forEach(jsonBody::put);
        return jsonBody;
    }

    @Override
    public void afterDeserialize(JSONObject json) {
        String modePath = json.optString("modePath");
        File modePathFile = new File(modePath);
        modeName = modePathFile.getName();
        System.out.println("解析模块：" + modePath);
        File pomFile = new File(modePathFile,"pom.xml");
        if(!pomFile.isFile()){
            System.out.println("指定路径下无pom.xml文件，无法解析 "+pomFile.getAbsolutePath());
            return;
        }
        resources  = new Resources();
        resources.deserialize(json);
        try {
            JSONObject pomObject =  XmlParser.parseXmlJson(pomFile).optJSONObject("project",new JSONObject());
            baseModeConfig = new BaseModeConfig();
            baseModeConfig.deserialize(pomObject);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void findDependencies(JSONObject dependencies){
        if(dependencies.has("dependency")){
            Object o = dependencies.get("dependency");
            if(o instanceof JSONArray){
                JSONArray jsonArray = (JSONArray) o;
                jsonArray.forEach(dependency -> {
                    Dependency d = new Dependency();
                    d.deserialize((JSONObject) dependency);
                    Mode.this.dependencies.add(d);
                });
            }else {
                Dependency d = new Dependency();
                d.deserialize((JSONObject) o);
                Mode.this.dependencies.add(d);
            }
        }
    }
    private void findModes(JSONObject modules){
        if(modules.has("module")){
            Object o = modules.get("module");
            if(o instanceof JSONArray){
                JSONArray jsonArray = (JSONArray) o;
                jsonArray.forEach(this::addMode);
            }else addMode(o);
        }
    }
    private void addMode(Object modeName){
        Mode mode = new Mode();
        mode.parent = Mode.this;
        mode.deserialize(resources.serialize().put("modePath",new File(getModePath(),modeName.toString()).getAbsolutePath()));
        modes.add(mode);
        mode.modeNames.add(modeName.toString());
        modeNames.addAll(mode.modeNames);
    }
    private void findJava() throws IOException {
        File file = new File(getModePath(), resources.resourcesPathJava);
        if (file.isDirectory())
            Files.walk(file.toPath())
                    .filter(Files::isRegularFile)
                    .filter(p -> p.toString().endsWith(".java"))
                    .forEach(p -> {
                        try {
                            String relativePath = file.toPath().relativize(p).toString();
                            String qualifiedName = relativePath
                                    .replace(File.separatorChar, '.')
                                    .substring(0, relativePath.length() - ".java".length());

                            int lastDotIndex = qualifiedName.lastIndexOf('.');
                            JSONObject json = new JSONObject();
                            json.put("packageName", lastDotIndex > 0 ?
                                    qualifiedName.substring(0, lastDotIndex) : "");
                            json.put("className", qualifiedName.substring(lastDotIndex + 1));
                            Java java = new Java();
                            java.deserialize(json);
                            javas.add(java);
                        } catch (Exception e) {
                            System.err.println("Error processing file: " + p + ", " + e.getMessage());
                        }
                    });
    }

    @Override
    public String toString() {
        return modeName ;
    }

    class BaseModeConfig implements ToJson{
        @JsonMark
        String version;
        @JsonMark
        String artifactId;
        @JsonMark
        String groupId;

        @Override
        public void deserializeBody(JSONObject json) {
            if(!json.has("groupId"))
                json.put("groupId",parent.baseModeConfig.groupId);
            if(!json.has("artifactId"))
                json.put("artifactId",parent.baseModeConfig.artifactId);
            if(!json.has("version"))
                json.put("version",parent.baseModeConfig.version);
            ToJson.super.deserializeBody(json);
        }

        @Override
        public void afterDeserialize(JSONObject json) {
            if(json.has("modules"))
                findModes(json.optJSONObject("modules",new JSONObject()));
            if(json.has("dependencies"))
                findDependencies(json.optJSONObject("dependencies",new JSONObject()));
            try {
                findJava();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
